package top.codedance.iotp.registy;

import top.codedance.iotp.client.Version;
import top.codedance.iotp.common.entity.ServerMeta;
import top.codedance.iotp.common.util.*;
import top.codedance.iotp.registy.distributed.RegistyServer;
import top.codedance.iotp.registy.distributed.TeamServer;
import top.codedance.iotp.registy.net.AdvancedHttpServer;
import top.codedance.iotp.registy.net.HttpServer;
import top.codedance.iotp.registy.net.configration.CliCmdOptionsConfig;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class RegistryApplication {

    public static final Long version = Long.valueOf(Version.VERSION); //server 版本
    public static CommandCli commandCli;
    public static ExecutorService pool = Executors.newFixedThreadPool(3); //线程池管理
    public static volatile List<String> onlineRegists = new ArrayList<>(); //在线的注册中心实例
    public static volatile Map<String, ServerMeta> servers = new HashMap<>(); //服务端元信息
    public static volatile Map<String, String> clients = new HashMap<>(); //服务端元信息
    public static ExecutorService workerPool = Executors.newCachedThreadPool(); //任务线程池
    private static Logger logger;
    public static Properties propConfigs;
    public static int port;
    public static boolean isMaster = false; //是否是主节点
    public static boolean isElectioning = false; //是否在选举
    public static long syncLastTimestamp = 0; //最后一次数据同步时间戳
    public static long bootTimestamp = 0; //本节点启动时间
    public static String masterHost;
    public static String visitUrl;
    private static boolean useAdvancedHttpServer = false;
    public static int ioThreads = Runtime.getRuntime().availableProcessors() * 2;
    public static int workerThreads = ioThreads * 4;

    public static void main(String[] args) {
        System.setProperty("java.net.preferIPv4Stack", "true");
        System.setProperty("io.netty.noPreferDirect", "true");
        System.setProperty("io.netty.tmpdir", OSinfo.isWindows() ? System.getProperty("java.io.tmpdir") : "/var/tmp");
        commandCli = new CommandCli(args, CliCmdOptionsConfig.getCmds());
        logger = RegistryApplication.getLogger(RegistryApplication.class);
        logger.debug("------------------------current work dir: " + SystemUtil.getRuntimeDirPath(RegistryApplication.class) + "------------------------");

        propConfigs = new Properties();
        try {
            propConfigs.load(RegistryApplication.class.getResourceAsStream("/application.properties"));
            String[] split = propConfigs.getProperty("registy.servers.hosts").split(",");
            if (split != null) {
                onlineRegists = new ArrayList<>(Arrays.asList(split));
            }
        } catch (IOException e) {
            logger.debug(e.getMessage());
        }
        port = commandCli.getArg("port", 10014);
        masterHost = visitUrl = commandCli.getArg("visitUrl", "http://127.0.0.1:" + port);
        useAdvancedHttpServer = commandCli.getArg("useAdvancedHttpServer", false);
        ioThreads = commandCli.getArg("ioThreads", Runtime.getRuntime().availableProcessors() * 2);
        workerThreads = commandCli.getArg("workerThreads", ioThreads * 4);

        run();
    }

    private static void run() {
        pool.submit((Runnable) () -> {
            while (true) {
                try {
                    if(useAdvancedHttpServer){
                        new AdvancedHttpServer("0.0.0.0", port).run();
                    }else {
                        new HttpServer(port).run();
                    }
                } catch (Exception ex) {
                    logger.debug(ex.getMessage());
                    RegistryApplication.syslogInfo("Start HttpServer fail!");
                }
                try {
                    Thread.sleep(5000);
                    RegistryApplication.syslogInfo("Restart HttpServer ... ");
                } catch (Exception ex) {}
            }
        });

        pool.submit((Runnable) () -> {
            while (true) {
                try {
                    new RegistyServer().run();
                } catch (Exception ex) {
                    logger.debug(ex.getMessage());
                    RegistryApplication.syslogInfo("Start RegistyServer fail!");
                }
                try {
                    Thread.sleep(1000);
                    RegistryApplication.syslogInfo("Restart RegistyServer ... ");
                } catch (Exception ex) {}
            }
        });

        pool.submit((Runnable) () -> {
            while (true) {
                try {
                    new TeamServer().run();
                } catch (Exception ex) {
                    logger.debug(ex.getMessage());
                    RegistryApplication.syslogInfo("Start RegistyServer fail!");
                }
                try {
                    Thread.sleep(1000);
                    RegistryApplication.syslogInfo("Restart RegistyServer ... ");
                } catch (Exception ex) {}
            }
        });

        try {
            Date d = new Date();
            bootTimestamp = d.getTime(); //设置启动时间
            pool.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            logger.info("thread pool finished, and restart task.");
        } finally {
            if (pool.isShutdown()) {
                pool = Executors.newFixedThreadPool(3);
                try {
                    Thread.sleep(2000);
                    RegistryApplication.syslogInfo("Restarting ... ");
                }catch (Exception ex){}
                run();
            }
        }
    }

    /**
     * 获取log对象并动态设置日志级别
     * @param clazz
     * @return
     */
    public static Logger getLogger(Class<?> clazz){
        Logger logger = Logger.getLogger(clazz);
        logger.setLevel(Level.toLevel(RegistryApplication.commandCli.getArg("logLevel", "ERROR")));
        return logger;
    }

    /**
     * 系统日志输出，任何情况下都打印
     * @param info
     */
    public static void syslogInfo(String info){
        System.out.println(info);
    }

    public static synchronized boolean checkServer(String ip){
        return servers.containsKey(ip);
    }

    public static synchronized void addServer(String ip, ServerMeta sm){
        servers.put(ip, sm);
    }

    public static synchronized void delServer(String ip){
        servers.remove(ip);
        System.out.println("删除server ["+ip+"]");
    }

    public static synchronized ServerMeta getServer(String ip){
        return servers.get(ip);
    }

    public static synchronized boolean checkClient(String deviceId){
        return clients.containsKey(deviceId);
    }

    public static synchronized void addClient(String deviceId, String ip){
        clients.put(deviceId, ip);
    }

    public static synchronized Map<String, ServerMeta> getServers(){
        return servers;
    }

    public static synchronized void delClient(String deviceId){
        clients.remove(deviceId);
        System.out.println("删除client ["+deviceId+"]");
    }

    public static synchronized void delClientForServer(String ip){
        Set<Map.Entry<String, String>> entries = clients.entrySet();
        for (Map.Entry<String, String> item : entries){
            if(item.getValue().equals(ip)){
                clients.remove(item.getKey());
                break;
            }
        }
    }

    public static synchronized int getClientCountForServerId(String ip){
        int i = 0;
        Set<Map.Entry<String, String>> entries = clients.entrySet();
        for (Map.Entry<String, String> item : entries){
            if(item.getValue().equals(ip)){
                i++;
            }
        }
        return i;
    }

    public static synchronized String getClient(String deviceId){
        return clients.get(deviceId);
    }

    public static boolean isLocalIp(String host){
        host = host.replaceAll("http://", "").replaceAll("https://", "");
        boolean result = false;
        result = host.matches("localhost:" + port) || host.matches("127.0.0.1:" + port);
        if(!result) {
            List<String> hostIps = MacUtils.getHostIp();
            for(String ip : hostIps){
                if((ip + ":" + port).equals(host)){
                    result = true;
                    break;
                }
            }
        }
        return result;
    }
}
